﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UI2DSpriteEx : UI2DSprite
{
//    private bool useSpriteInnerBorder = false;

    public SpriteEx spriteEx;
    public SpriteEx preloadSpriteEx;
    public SpriteEx nextSpriteEx;
    public string path;
    public UIFixedAspectMode mFixedAspectMode = UIFixedAspectMode.none;
    bool loading;

    public bool fixScreenEdge;
    public override Vector4 drawingDimensions
    {
        get
        {
            Vector4 result = base.drawingDimensions;

            if (mFixedAspectMode != UIFixedAspectMode.none)
            {
                Texture tex = mainTexture;
                if (tex == null) return result;
                Rect outer = (base.sprite2D != null) ? base.sprite2D.textureRect : new Rect(0f, 0f, tex.width, tex.height);
                float outerRatio = outer.width / outer.height;
                float w = result.z - result.x;
                float h = result.w - result.y;
                float dimensionsRatio = w / h;
                if ((mFixedAspectMode == UIFixedAspectMode.fill && outerRatio < dimensionsRatio) || (mFixedAspectMode == UIFixedAspectMode.fit && outerRatio > dimensionsRatio))
                {
                    float hh = w / outerRatio / 2;
                    float yc = (result.y + result.w) * 0.5f;
                    result.y = yc - hh;
                    result.w = yc + hh;
                }
                else if ((mFixedAspectMode == UIFixedAspectMode.fill && outerRatio > dimensionsRatio) || (mFixedAspectMode == UIFixedAspectMode.fit && outerRatio < dimensionsRatio))
                {
                    float hw = outerRatio * h / 2;
                    float xc = (result.x + result.z) * 0.5f;
                    result.x = xc - hw;
                    result.z = xc + hw;
                }
            }

//            if (fixScreenEdge)
//            {
//                result.z = Mathf.Ceil(result.z);
//                result.y = Mathf.Floor(result.y);
//            }
            return result;
        }
    }

	protected override void OnUpdate ()
	{
        Sprite sprite = this.sprite2D;
        if (nextSpriteEx != null && nextSpriteEx.sprite != null)
        {
            if (nextSpriteEx != this.spriteEx)
            {
                SetSprite(nextSpriteEx);
            }

            nextSpriteEx = null;
        }
		sprite = this.sprite2D;

//		UIWidget.OnUpdate (); begin
		if (panel == null) CreatePanel();
		#if UNITY_EDITOR
		else if (!mPlayMode) ParentHasChanged();
		#endif
//		UIWidget.OnUpdate (); end

		if (fixedAspect)
		{
			Texture tex = mainTexture;

			if (tex != null)
			{
				float w = Mathf.RoundToInt(sprite.rect.width);
				float h = Mathf.RoundToInt(sprite.rect.height);
				int padLeft = Mathf.RoundToInt(sprite.textureRectOffset.x);
				int padBottom = Mathf.RoundToInt(sprite.textureRectOffset.y);
				int padRight = Mathf.RoundToInt(sprite.rect.width - sprite.textureRect.width - sprite.textureRectOffset.x);
				int padTop = Mathf.RoundToInt(sprite.rect.height - sprite.textureRect.height - sprite.textureRectOffset.y);

				w += padLeft + padRight;
				h += padTop + padBottom;

				Vector4 tempPadding = padding;

				//fix fixed aspect begin
				w += tempPadding.x + tempPadding.z;
				h += tempPadding.y + tempPadding.w;
				//fix fixed aspect end

				float widgetWidth = mWidth;
				float widgetHeight = mHeight;
				float widgetAspect = widgetWidth / widgetHeight;
				float textureAspect = (float)w / h;

				if (textureAspect < widgetAspect)
				{
					float x = (widgetWidth - widgetHeight * textureAspect) / widgetWidth * 0.5f;
					drawRegion = new Vector4(x, 0f, 1f - x, 1f);
				}
				else
				{
					float y = (widgetHeight - widgetWidth / textureAspect) / widgetHeight * 0.5f;
					drawRegion = new Vector4(0f, y, 1f, 1f - y);
				}
			}
		}
	}

//    public override Vector4 border
//    {
//        get
//        {
//            if (useSpriteInnerBorder)
//            {
//                if (sprite2D == null)
//                {
//                    return Vector4.zero;
//                }
//                return base.sprite2D.border;
//            }
//            return base.border;
//        }
//    }

    public Vector4 padding
    {
        get
        {
            if (spriteEx != null && spriteEx.spriteInfo != null)
            {
                return spriteEx.spriteInfo.padding;
            }
            return Vector4.zero;
        }
    }

    public SpriteEx GetSprite()
    {
        if(this.spriteEx == null || this.spriteEx.sprite == null)
        {
            if(this.sprite2D != null)
            {
                if(this.spriteEx == null)
                {
                    this.spriteEx = new SpriteEx();
                }

                this.spriteEx.sprite = this.sprite2D;
            }
        }

        return this.spriteEx;
    }

    public void SetSprite(SpriteEx spriteEx)
    {
        if (this == null)
        {
            return;
        }

        ResourceManager.CancelAsyncTask(this.path, LoadSpriteExCallback);
        this.path = null;

        RealSetSprite(spriteEx);
    }

    void RealSetSprite(SpriteEx spriteEx)
    {
        this.spriteEx = spriteEx;
        if (spriteEx != null)
        {
            this.sprite2D = spriteEx.sprite;
            if (spriteEx.sheetInfo != null)
            {
                this.material = spriteEx.sheetInfo.uiMat;
            }
            else
            {
                //            this.material = null;
            }

            if (spriteEx.sprite != null)
            {
                this.border = spriteEx.sprite.border;
            }
        }
        else
        {
            this.sprite2D = null;
        }
    }

    public void SetSprite(string path)
    {
        SetSprite(path, null);
    }

    public void SetSprite(string path, SpriteEx preloadSprite)
    {
        if (this.path == path)
        {
            return;
        }

        if (preloadSprite != null)
        {
            this.preloadSpriteEx = preloadSprite;
        }
            
        SetSprite(this.preloadSpriteEx);


        ResourceManager.CancelAsyncTask(this.path, LoadSpriteExCallback);
        this.path = path;
        if (string.IsNullOrEmpty(this.path))
        {
            return;
        }

        loading = true;
        ResourceManager.LoadSpriteExAsync(path, LoadSpriteExCallback);
    }

    void LoadSpriteExCallback(object obj, string path)
    {
        loading = false;
        SpriteEx spriteEx = obj as SpriteEx;
        if(spriteEx == null)
        {
            return;
        }

        if(this.path != path)
        {
            return;
        }
        
        RealSetSprite(spriteEx);
    }

    protected void Fill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols, Rect outer, Rect inner, Vector4 ratePadding, Vector4 padding)
    {
        mOuterUV = outer;
        mInnerUV = inner;

        switch (type)
        {
            case Type.Simple:
                SimpleFill(verts, uvs, cols, ratePadding, padding);
                break;

            case Type.Sliced:
                SlicedFill(verts, uvs, cols, ratePadding, padding);
                break;

            default:
                base.Fill(verts, uvs, cols, outer, inner);
                break;
        }
    }



    /// <summary>
    /// Regular sprite fill function is quite simple.
    /// </summary>

    void SimpleFill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols, Vector4 ratePadding, Vector4 padding)
    {
        Vector4 v = drawingDimensions;
        Vector4 u = drawingUVs;
        Color gc = drawingColor;

        float w = v.z - v.x;
        float h = v.w - v.y;
        v.x += ratePadding.x * w;
        v.z -= ratePadding.z * w;
        v.y += ratePadding.y * h;
        v.w -= ratePadding.w * h;
        verts.Add(new Vector3(v.x, v.y));
        verts.Add(new Vector3(v.x, v.w));
        verts.Add(new Vector3(v.z, v.w));
        verts.Add(new Vector3(v.z, v.y));

        uvs.Add(new Vector2(u.x, u.y));
        uvs.Add(new Vector2(u.x, u.w));
        uvs.Add(new Vector2(u.z, u.w));
        uvs.Add(new Vector2(u.z, u.y));

        if (!mApplyGradient)
        {
            cols.Add(gc);
            cols.Add(gc);
            cols.Add(gc);
            cols.Add(gc);
        }
        else
        {
            AddVertexColours(cols, ref gc, 1, 1);
            AddVertexColours(cols, ref gc, 1, 2);
            AddVertexColours(cols, ref gc, 2, 2);
            AddVertexColours(cols, ref gc, 2, 1);
        }
    }

    /// <summary>
    /// Sliced sprite fill function is more complicated as it generates 9 quads instead of 1.
    /// </summary>

    void SlicedFill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols, Vector4 ratePadding, Vector4 padding)
    {
        Vector4 br = border * pixelSize;

        if (br.x == 0f && br.y == 0f && br.z == 0f && br.w == 0f)
        {
            SimpleFill(verts, uvs, cols, ratePadding, padding);
            return;
        }

        Color gc = drawingColor;
        Vector4 v = drawingDimensions;

        mTempPos[0].x = v.x;
        mTempPos[0].y = v.y;
        mTempPos[3].x = v.z;
        mTempPos[3].y = v.w;



        if (mFlip == Flip.Horizontally || mFlip == Flip.Both)
        {
            mTempPos[0].x += padding.z;
            mTempPos[3].x -= padding.x;

            mTempPos[1].x = mTempPos[0].x + br.z;
            mTempPos[2].x = mTempPos[3].x - br.x;

            mTempUVs[3].x = mOuterUV.xMin;
            mTempUVs[2].x = mInnerUV.xMin;
            mTempUVs[1].x = mInnerUV.xMax;
            mTempUVs[0].x = mOuterUV.xMax;
        }
        else
        {
            mTempPos[0].x += padding.x;
            mTempPos[3].x -= padding.z;

            mTempPos[1].x = mTempPos[0].x + br.x;
            mTempPos[2].x = mTempPos[3].x - br.z;

            mTempUVs[0].x = mOuterUV.xMin;
            mTempUVs[1].x = mInnerUV.xMin;
            mTempUVs[2].x = mInnerUV.xMax;
            mTempUVs[3].x = mOuterUV.xMax;
        }

        if (mFlip == Flip.Vertically || mFlip == Flip.Both)
        {
            mTempPos[0].y += padding.w;
            mTempPos[3].y -= padding.y;

            mTempPos[1].y = mTempPos[0].y + br.w;
            mTempPos[2].y = mTempPos[3].y - br.y;

            mTempUVs[3].y = mOuterUV.yMin;
            mTempUVs[2].y = mInnerUV.yMin;
            mTempUVs[1].y = mInnerUV.yMax;
            mTempUVs[0].y = mOuterUV.yMax;
        }
        else
        {
            mTempPos[0].y += padding.y;
            mTempPos[3].y -= padding.w;

            mTempPos[1].y = mTempPos[0].y + br.y;
            mTempPos[2].y = mTempPos[3].y - br.w;

            mTempUVs[0].y = mOuterUV.yMin;
            mTempUVs[1].y = mInnerUV.yMin;
            mTempUVs[2].y = mInnerUV.yMax;
            mTempUVs[3].y = mOuterUV.yMax;
        }

        for (int x = 0; x < 3; ++x)
        {
            int x2 = x + 1;

            for (int y = 0; y < 3; ++y)
            {
                if (centerType == AdvancedType.Invisible && x == 1 && y == 1) continue;

                int y2 = y + 1;

                verts.Add(new Vector3(mTempPos[x].x, mTempPos[y].y));
                verts.Add(new Vector3(mTempPos[x].x, mTempPos[y2].y));
                verts.Add(new Vector3(mTempPos[x2].x, mTempPos[y2].y));
                verts.Add(new Vector3(mTempPos[x2].x, mTempPos[y].y));

                uvs.Add(new Vector2(mTempUVs[x].x, mTempUVs[y].y));
                uvs.Add(new Vector2(mTempUVs[x].x, mTempUVs[y2].y));
                uvs.Add(new Vector2(mTempUVs[x2].x, mTempUVs[y2].y));
                uvs.Add(new Vector2(mTempUVs[x2].x, mTempUVs[y].y));

                if (!mApplyGradient)
                {
                    cols.Add(gc);
                    cols.Add(gc);
                    cols.Add(gc);
                    cols.Add(gc);
                }
                else
                {
                    AddVertexColours(cols, ref gc, x, y);
                    AddVertexColours(cols, ref gc, x, y2);
                    AddVertexColours(cols, ref gc, x2, y2);
                    AddVertexColours(cols, ref gc, x2, y);
                }
            }
        }
    }
    public override void OnFill (List<Vector3> verts, List<Vector2> uvs, List<Color> cols)
    {
        Texture tex = mainTexture;
        if (tex == null) return;

        Rect outer = (base.sprite2D != null) ? base.sprite2D.textureRect : new Rect(0f, 0f, tex.width, tex.height);

        if(outer.width <= 0 || outer.height <= 0)
        {
            throw new System.Exception("outer rect size error: " + outer + " | " + ((base.sprite2D != null) ? base.sprite2D.packingMode.ToString(): "null") + " | " + base.sprite2D);
        }

        Rect realOuter = outer;
        realOuter.x -= padding.x;
        realOuter.y -= padding.y;
        realOuter.width += padding.x + padding.z;
        realOuter.height += padding.y + padding.w;

        Vector4 ratePadding = padding;
        ratePadding.x /= realOuter.width;
        ratePadding.z /= realOuter.width;
        ratePadding.y /= realOuter.height;
        ratePadding.w /= realOuter.height;


        Rect inner = outer;
        Vector4 br = border;
        
        inner.xMin += br.x;
        inner.yMin += br.y;
        inner.xMax -= br.z;
        inner.yMax -= br.w;

        float w = 1f / tex.width;
        float h = 1f / tex.height;

        outer.xMin *= w;
        outer.xMax *= w;
        outer.yMin *= h;
        outer.yMax *= h;

        inner.xMin *= w;
        inner.xMax *= w;
        inner.yMin *= h;
        inner.yMax *= h;

        int offset = verts.Count;
        Fill(verts, uvs, cols, outer, inner, ratePadding, padding);

        if (onPostFill != null)
            onPostFill(this, offset, verts, uvs, cols);
    }

	public static void UpdateAll()
	{
		UI2DSpriteEx[] sprites = Resources.FindObjectsOfTypeAll<UI2DSpriteEx>();
		for (int i = 0; i < sprites.Length; i++)
		{
			UI2DSpriteEx sprite = sprites[i];
			string temp = sprite.path;
			if (string.IsNullOrEmpty(temp))
			{
				continue;
			}

			sprite.path = null;
			sprite.SetSprite(temp);
		}
	}

    protected override void OnDestroy ()
    {
        if (loading)
        {
            ResourceManager.CancelAsyncTask(this.path, LoadSpriteExCallback);
            loading = false;
        }

        base.OnDestroy();
    }
}
